﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Controls;
using System.IO;
using System.Windows;
using System.Windows.Forms;
using System.Windows.Input;

namespace Shoozla.ViewModel
{
    class FolderExplorerViewModel : ViewModelBase
    {

        #region Variables

        private readonly object _dummyNode = null;
        public Dictionary<String, DirectoryInfo> InternalSelectedFolders;
        //public event FolderChangedDelegate SelectetdFolderChanged;
        //public event FoldersChangedDelegate AddedFolders;
        //public event FolderChangedDelegate RemovedFolder;
        //public delegate void FolderChangedDelegate(DirectoryInfo folder);
        //public delegate void FoldersChangedDelegate(List<DirectoryInfo> folder);
        private System.Windows.Controls.TreeView foldersTree;
        private bool IgnoreEvents = false;

        #endregion

        public FolderExplorerViewModel(System.Windows.Controls.TreeView treeSource, Window view) : base(view,App.MainView)
        {
            foldersTree = treeSource;
            _dummyNode = new object();
            ShowHint = true;
            InternalSelectedFolders = new Dictionary<String, DirectoryInfo>();

            ParentFolder = AppModel.ParentFolder;            
            SetPreviousStatus();

        }

        private void SetPreviousStatus() {
           
            if(ParentFolder==null || AppModel.FoldersSelected == null)
                return;

            foreach(DirectoryInfo d in AppModel.FoldersSelected){
                IgnoreEvents = true;
                ExpandUntil(d, (TreeViewItem)foldersTree.Items[0]);
            }
            //IgnoreEvents = false;
        }

        #region Properties

        private bool _showHint;
        public bool ShowHint
        {
            get { return _showHint; }
            set
            {
                if (value != _showHint)
                {
                    _showHint = value;
                    OnPropertyChanged("ShowHint");
                }
            }
        } //decide if display a visual message to the user, for help purpose


        private DirectoryInfo _parentFolder;
        public DirectoryInfo ParentFolder
        {
            get { return _parentFolder; }
            set
            {
                if (value == null)
                    return;
                ShowHint = false;
                _parentFolder = value;
                AddFolderToView(_parentFolder);
                OnPropertyChanged("ParentFolder");
            }
        }

        #endregion

        #region Commands

        private RelayCommand _CmdCancel;
        public RelayCommand CmdCancel
        {
            get
            {
                if (_CmdCancel == null)
                {
                    _CmdCancel = new RelayCommand(param => DoCmdCancel(), (c) => true);
                }
                return _CmdCancel;
            }
        }

        private RelayCommand _CmdApply;
        public RelayCommand CmdApply
        {
            get
            {
                if (_CmdApply == null)
                {
                    _CmdApply = new RelayCommand(param => DoCmdApply(), (c) => {
                        if (InternalSelectedFolders.Count > 0)
                            return true;
                        else
                            return false;
                    });
                }
                return _CmdApply;
            }
        }

        private RelayCommand _CmdOpenFolderDialog;
        public RelayCommand CmdOpenFolderDialog
        {
            get
            {
                if (_CmdOpenFolderDialog == null)
                {
                    _CmdOpenFolderDialog = new RelayCommand(param => DoCmdOpenFolderDialog(), (c) => true);
                }
                return _CmdOpenFolderDialog;
            }
        }


        #endregion

        #region DoCommands

        private void DoCmdCancel()
        {
            View.DialogResult = false;
            OnRequestClose();
        }

        private void DoCmdApply()
        {
            //change the current selected folders, to trigger the new search
            if (InternalSelectedFolders.Count > 0)
            {
                AppModel.ParentFolder = ParentFolder;
                AppModel.FoldersSelected = InternalSelectedFolders.Values.ToList<DirectoryInfo>();
                View.DialogResult = true;
            }
            else
                View.DialogResult = false;
            
            OnRequestClose();                
        }

        private void DoCmdOpenFolderDialog()
        {
            IgnoreEvents = false;
            FolderBrowserDialog folderDialog = new FolderBrowserDialog();
            folderDialog.Description = "Select a folder with Mp3 files";
            var res = folderDialog.ShowDialog();
            if (res == DialogResult.OK)
            {
                var dir = new DirectoryInfo(folderDialog.SelectedPath);                
                ParentFolder = dir;                
            }
        }

        #endregion

     
        #region TreeViewMethods

        public void folder_UnChecked(object sender, RoutedEventArgs e)
        {
            try
            {
                System.Windows.Controls.CheckBox c = sender as System.Windows.Controls.CheckBox;

                TreeViewItem rootItem = (TreeViewItem)c.Tag;
                DirectoryInfo dir = (DirectoryInfo)rootItem.Tag;

                if (InternalSelectedFolders.ContainsKey(dir.FullName))
                    InternalSelectedFolders.Remove(dir.FullName);

                //if (RemovedFolder != null)
                //    RemovedFolder.BeginInvoke((DirectoryInfo)rootItem.Tag, null, null);

                if (IgnoreEvents)
                    return; //doing recursion, not notifying the main

                //uncheck all the children
                CheckUntil(rootItem, false);
            }
            catch (NullReferenceException nex)
            { 
                //parent node has a null tag
            }

        }

        public void folder_Checked(object sender, RoutedEventArgs e)
        {
            try {
                System.Windows.Controls.CheckBox c = sender as System.Windows.Controls.CheckBox;

                TreeViewItem rootItem = (TreeViewItem)c.Tag;
                DirectoryInfo dir = (DirectoryInfo)rootItem.Tag;

                if (InternalSelectedFolders.ContainsKey(dir.FullName))
                    return;
                else
                    //this is a new folder, continue to read and check all the children
                    InternalSelectedFolders.Add(dir.FullName, dir);

                if (IgnoreEvents)
                    return; //doing recursion, not notifying the main

                //check all the children
                IgnoreEvents = true;
                List<DirectoryInfo> children = CheckUntil(rootItem, true);
                foreach (DirectoryInfo d in children)
                    if (!InternalSelectedFolders.ContainsKey(d.FullName))
                        InternalSelectedFolders.Add(d.FullName, d);

                IgnoreEvents = false;

                //if (AddedFolders != null)
                //    AddedFolders.BeginInvoke(InternalSelectedFolders.Values.ToList<DirectoryInfo>(), null, null);

                rootItem.IsSelected = true;
            }
            catch (NullReferenceException nex)
            {
                //parent node has a null tag
            }
        }

        void folder_Expanded(object sender, RoutedEventArgs e)
        {
            TreeViewItem item = (TreeViewItem)sender;

            if (sender == null)
                return;

            if (item.Items.Count == 1 && item.Items[0] == _dummyNode)
            {
                item.Items.Clear();
                try
                {
                    foreach (DirectoryInfo subFolder in ((DirectoryInfo)item.Tag).EnumerateDirectories())
                    {
                        TreeViewItem subitem = new TreeViewItem();
                        subitem.Header = subFolder.Name;
                        subitem.Tag = subFolder;
                        subitem.Items.Add(_dummyNode);
                                                
                        TreeViewItemProps.SetIsChecked(subitem, false);

                        //to not insert duplicate handlers
                        //subitem.Expanded -= folder_Expanded;
                        //subitem.Collapsed -= folder_Collpased;

                        //subitem.Expanded += folder_Expanded;
                        //subitem.Collapsed += folder_Collpased;
                        item.Items.Add(subitem);
                    }
                }
                catch { }
                finally
                {
                    // item.IsSelected = true;
                }
            }
        }

        void folder_Collpased(object sender, RoutedEventArgs e)
        {
        }

        //public void foldersTree_SelectedItemChanged(object sender, RoutedPropertyChangedEventArgs<object> e)
        //{
        //    if (SelectetdFolderChanged != null)
        //    {
        //        TreeViewItem item = (TreeViewItem)foldersTree.SelectedItem;
        //        DirectoryInfo selectedFolder = (DirectoryInfo)item.Tag;
        //        SelectetdFolderChanged.BeginInvoke(selectedFolder, null, null);
        //    }
        //}

        /// <summary>
        /// Entrypoint to add a folder to the tree
        /// </summary>
        public void AddFolderToView(DirectoryInfo folder)
        {
            UIHelper.SetWaitCursor();

            try
            {
                if (folder != null)
                {
                    //int index = FolderAlreadyRead(folder);
                    //if (index >= 0)
                    //{
                    //    //folder already present 
                    //    ExpandUntil(folder, (TreeViewItem)foldersTree.Items[index]);
                    //    return;
                    //}

                    //cleanup of cache
                    InternalSelectedFolders.Clear();
                    foldersTree.Items.Clear();

                    //add the item as root
                    TreeViewItem mainItem = new TreeViewItem();
                    mainItem.Header = folder.Name;
                    mainItem.Tag = folder;
                    mainItem.Items.Add(_dummyNode);
                    mainItem.Expanded += folder_Expanded;
                    mainItem.Collapsed += folder_Collpased;

                    // Apply the attached property so that 
                    // the triggers know that this is root item.
                    TreeViewItemProps.SetIsRootLevel(mainItem, true);
                    TreeViewItemProps.SetIsChecked(mainItem, false);

                    foldersTree.Items.Add(mainItem);
                    mainItem.IsSelected = true;
                    mainItem.IsExpanded = true;
                    // folder_Expanded(mainItem, null);
                    ReadChildren(mainItem); //read all the children, but not display
                    ShowHint = false;
                }
            }
            catch (UnauthorizedAccessException accEx)
            {
                AppModel.LogEntry("[ERROR] AddFolderToView: " + accEx.Message);
            }
            finally
            {
                UIHelper.SetNormalCursor();

            }
        }

        private int FolderAlreadyRead(DirectoryInfo folder)
        {
            foreach (TreeViewItem i in foldersTree.Items)
            {
                DirectoryInfo dir = i.Tag as DirectoryInfo;
                if (dir != null)
                {
                    if (folder.FullName.StartsWith(dir.FullName))
                        return foldersTree.Items.IndexOf(i);
                }
            }
            return -1;
        }

        private void ExpandUntil(DirectoryInfo target, TreeViewItem sourceItem)
        {
            DirectoryInfo dir1 = sourceItem.Tag as DirectoryInfo;
            if (dir1 != null)
            {
                if (target.FullName == dir1.FullName)
                {
                    TreeViewItemProps.SetIsChecked(sourceItem, true);
                    return;
                }
            }

            foreach (TreeViewItem i in sourceItem.Items)
            {
                DirectoryInfo dir = i.Tag as DirectoryInfo;
                if (dir != null)
                {
                    if (target.FullName == dir.FullName)
                    {
                        TreeViewItemProps.SetIsChecked(i, true);
                        return;
                    }

                    if (target.FullName.StartsWith(dir.FullName))
                    {
                        i.IsExpanded = true;
                        ExpandUntil(target, i);
                    }
                }
            }
        }

        private void ReadChildren(TreeViewItem item)
        {
            folder_Expanded(item, null);

            for (int i = 0; i < item.Items.Count; i++)
            {
                try
                {
                    TreeViewItem currentItem = item.Items[i] as TreeViewItem;
                    //currentItem.IsExpanded = false;
                    ReadChildren(currentItem);
                }
                catch
                {
                    continue;
                }
            }
        }

        private List<DirectoryInfo> CheckUntil(TreeViewItem item, bool IsChecked)
        {
            List<DirectoryInfo> folders = new List<DirectoryInfo>();
            for (int i = 0; i < item.Items.Count; i++)
            {
                try
                {
                    TreeViewItem currentItem = item.Items[i] as TreeViewItem;
                    DirectoryInfo dir = currentItem.Tag as DirectoryInfo;
                    folders.Add(dir);
                    TreeViewItemProps.SetIsChecked(currentItem, IsChecked);
                    folders.AddRange(CheckUntil(currentItem, IsChecked));
                }
                catch
                {
                    continue;
                }
            }
            return folders;
        }
        #endregion

    }
}
